﻿using System;
using biz.ritter.javapi.lang;
using Nofs.Net.Common.Interfaces.Domain;
using biz.ritter.javapi.util;
using Db4objects.Db4o;

namespace Nofs.Net.nofs.Db4o
{

    public class Db4oSaveThread : Thread
    {

        private class WorkRequest
        {
            public IFileObjectStat stat;
            public bool delete = false;
            public WorkRequest(IFileObjectStat stat)
            {
                this.stat = stat;
            }
        }

        private Deque<WorkRequest> _queue = new LinkedList<WorkRequest>();
        private bool _keepGoing = true;
        private bool _isWorking = false;
        private IObjectContainer _db;
        private Map<UUID, IFileObjectStat> _cache;

        public Db4oSaveThread(IObjectContainer db)
        {
            _db = db;
            _cache = new HashMap<UUID, IFileObjectStat>();
        }

        public void Sync() //throws Exception 
        {
            lock (_queue)
            {
                while (_queue.size() > 0 || _isWorking)
                {
                    _queue.wait(10);
                }
            }
        }

        public void Kill() // throws Exception 
        {
            _keepGoing = false;
            Sync();

            join();
        }

        public void Enqueue(IFileObjectStat stat) //throws Exception 
        {
            lock (_queue)
            {
                _queue.addLast(new WorkRequest(stat.Clone()));
            }
        }

        public void Delete(IFileObjectStat stat) //throws Exception 
        {
            lock (_queue)
            {
                WorkRequest req = new WorkRequest(stat.Clone());
                req.delete = true;
                _queue.addLast(req);
            }
        }

        private Collection<WorkRequest> Dequeue() //throws Exception 
        {
            Collection<WorkRequest> stats = new LinkedList<WorkRequest>();
            lock (_queue)
            {
                while (_queue.size() == 0 && _keepGoing)
                {
                    _queue.wait(10);
                }
                while (_queue.size() > 0)
                {
                    stats.add(_queue.removeFirst());
                }
                _isWorking = true;
            }
            return stats;
        }

        private bool KeepRunning()
        {
            lock (_queue)
            {
                return _keepGoing || _queue.size() > 0;
            }
        }

        //@Override
        public override void run()
        {
            while (KeepRunning())
            {
                try
                {
                    _isWorking = false;
                    foreach (WorkRequest workReq in new LinkedList<WorkRequest>(Dequeue()))
                    {
                        IFileObjectStat localStat;
                        if (_cache.containsKey(workReq.stat.GetID()))
                        {
                            localStat = _cache.remove(workReq.stat.GetID());
                        }
                        else
                        {
                            localStat = workReq.stat;
                        }
                        if (workReq.delete)
                        {
                            _db.Delete(localStat);
                            _db.Commit();
                        }
                        else
                        {
                            _cache.put(localStat.GetID(), localStat);
                            _db.Store(localStat);
                            _db.Commit();
                            Console.WriteLine("Saved stat with ID = " + localStat.GetID().toString());
                        }
                    }
                }
                catch (System.Exception e)
                {
                    Console.WriteLine(e);
                    //e.printStackTrace();
                }
            }
            _isWorking = false;
        }
    }

}
